package main

import "fmt"

//结构体定义: type 结构体名 struct
//结构体名、字段名和方法名同样遵循大小写的访问规则

func main() {
	//initStruct()

	//structReceiver()

	//typeDefinition()

	//typeAlias()

	typeCombination()
}

// 结构体初始化
// Go语言没有构造器,创建对象的语法就是 &Struct{}
func initStruct() {
	//方式一: 通过指针初始化
	//u1是指向User对象的指针
	u1 := &User{}
	println("u1 = ", u1) //打印的值是一个地址

	//方式二: 通过值初始化,未被显式赋值的字段都是默认零值
	u2 := User{}
	fmt.Printf("u2 = %+v\n", u2)

	//方式三: 只定义变量,不初始化,对象也会被赋予默认零值
	var u3 User
	fmt.Printf("u3 = %+v\n", u3)

	//方式四: 通过内置的new函数初始化,对象同样会被赋予默认零值
	u4 := new(User)
	fmt.Printf("u4 = %+v\n", u4)

	//方式五: 显式初始化,推荐采用Field:Value的形式为字段赋值
	u5 := &User{
		ID:   1,
		Name: "Tom",
		Age:  18,
	}
	fmt.Printf("u5 = %+v\n", u5)
}

// 结构体接收器
// Go语言的传参都是值传递,如果采用值接收器,那么会进行值拷贝,生成了一个新对象,无法修改原对象的字段
// 无论是指针还是结构体本身,都既可以调用指针接收器,也可以调用值接收器,关键在于接收器的定义
func structReceiver() {
	u := &User{
		ID:   1,
		Name: "Tom",
		Age:  18,
	}

	u.ChangeAge(20)       //值接收器,无法修改
	u.ChangeName("Jerry") //指针接收器,可以修改
	fmt.Printf("u = %+v\n", u)
}

// 通过Type Definition定义的新类型
func typeDefinition() {
	//创建底层类型的对象
	u := UnderlyingType{}

	//创建Type Definition类型的对象
	n := NewDefinitionType{}

	//因为这两个类型本质(Identical)相同,因此可以通过显式的类型转换,进行互相赋值
	u = UnderlyingType(n)
	u.UnderlyingTypeHello()
}

// 通过Type Alias类型别名,定义完全等价的类型
func typeAlias() {
	//创建底层类型的对象
	o := OriginType{}

	//创建Type Alias类型的对象
	a := AliasType{}
	a.OriginTypeFunc()

	//两个类型完全等价,可以直接互相赋值
	o = a
	o.OriginTypeFunc()
}

// 类型组合
func typeCombination() {
	c := &C{}

	//因为类型C组合了A和B,因此可以直接调用A和B中的方法
	c.AFunc()
	c.BFunc()

	//组合不是继承,它没有多态的概念,当调用同名方法时,仍然是调用当前实际类型的方法
	c.Hello() //c hello

	//也可以指定调用组合类中的方法
	c.A.Hello() //a hello
}
